package edu.lnu.ums.dal.impl;

import java.util.List;

import edu.lnu.ums.dal.IGenericUMSObjectDAO;
import edu.lnu.ums.entities.core.LinkToAnotherDB;
import edu.lnu.ums.entities.core.UMSObject;
import java.util.ArrayList;
import java.util.Collections;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

public class GenericUMSObjectDAO<T extends UMSObject> extends AbstractConcurrentAccessGenericDAO<T> implements IGenericUMSObjectDAO<T>{
    public GenericUMSObjectDAO(Class<T> cls) {
       super(cls); 
    }

    @Override
    public void delete(UMSObject obj) {
        deleteWithChildren(obj);
    }

    @Override
    public void deleteWithChildren(UMSObject detachedObj) {
        EntityManager em = detachedObj.getFactory().createEntityManager();
        em.getTransaction().begin();
        UMSObject obj = (UMSObject) em.find(detachedObj.getClass(), detachedObj.getId());
        
        if (obj.getParent() != null) {
            obj.getParent().getChildren().remove(obj);
            obj.setParent(null);
        }

        em.remove(obj);

        em.getTransaction().commit();
    }

/*
    public void deleteAndAssignChildrenToRoot(UMSObject ums) {
        UMSObject root = getRoot();
        deleteAndAssignChildrenToNode(ums, root);

    }

    public void deleteAndAssignChildrenToNode(UMSObject detachedObj, UMSObject detachedNode) {
        UMSObject obj = (UMSObject) session.get(detachedObj.getClass(), detachedObj.getId());
        UMSObject node = (UMSObject) session.get(detachedNode.getClass(), detachedNode.getId());
        
        List<UMSObject> children = obj.getChildren();

        if (children == null || children.size() == 0) {
            deleteWithChildren(obj);
        } else {
            obj.setChildren(new ArrayList<UMSObject>());
            
            for (UMSObject child : children) {
                child.setParent(node);
            }

            session.delete(obj);
        }
    }
*/
    @Override
    @SuppressWarnings("unchecked")
    public UMSObject getRoot(EntityManagerFactory factory) {
        EntityManager em = factory.createEntityManager();

        List<UMSObject> list = 
            em.createQuery("from UMSObject where parent is null").getResultList();
        setEntityManagerFactoryToEntityList(factory, list);
        if (list.size() == 1) {
            return list.get(0);
        }
        
        if (list.size() == 0) {
            em.getTransaction().begin();
            UMSObject root = UMSObject.createRoot();
            em.persist(root);
            em.getTransaction().commit();
            setEntityManagerFactoryToEntity(factory, root);
            return root;
        }
        
        throw new IllegalStateException("There cann't be more than one root");
    }

    @Override
    public void changeParent(UMSObject child, UMSObject newParent) {
        if (child.getFactory() != newParent.getFactory()) {
            throw new RuntimeException("Child and new parent belong to different databases!");
        }
        
        EntityManager em = child.getFactory().createEntityManager();
        em.getTransaction().begin();
        em.createNativeQuery("update UMSObject set parent = :parent where id = :id")
                .setParameter("parent", newParent.getId())
                .setParameter("id", child.getId())
                .executeUpdate();
        em.getTransaction().commit();
        em.close();
    }

    @Override
    public List<UMSObject> getChildren(UMSObject object) {
        if (object instanceof LinkToAnotherDB) {
            EntityManagerFactory factory = ((LinkToAnotherDB)object).getAnotherDBFactory();
            if (factory == null) {
                return Collections.EMPTY_LIST;
            }
            
            UMSObject root = this.getRoot(factory);
            List<UMSObject> retList = new ArrayList<UMSObject>();
            retList.add(root);
            return retList;
            
        } else {
            EntityManager em = object.getFactory().createEntityManager();
            List<UMSObject> children = em.createQuery("from UMSObject where parent.id = :id")
                .setParameter("id", object.getId()).getResultList();
            setEntityManagerFactoryToEntityList(object.getFactory(), children);
            return children;
        }
    }

    @Override
    public String getOrderByQuery() {
        return "order by nodeName";
    }
}
